001    /*
002     * Copyright 2005 Stephen J. McConnell
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.tools.tasks;
020    
021    import java.io.File;
022    import java.io.OutputStream;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.net.URI;
026    import java.util.ArrayList;
027    import java.util.List;
028    
029    import net.dpml.lang.Classpath;
030    import net.dpml.lang.Category;
031    import net.dpml.lang.Info;
032    import net.dpml.lang.Part;
033    import net.dpml.lang.PartDecoder;
034    
035    import net.dpml.library.Type;
036    import net.dpml.library.Resource;
037    
038    import net.dpml.transit.Artifact;
039    
040    import net.dpml.tools.Context;
041    
042    import net.dpml.util.DefaultLogger;
043    
044    import org.apache.tools.ant.Project;
045    import org.apache.tools.ant.BuildException;
046    
047    import org.w3c.dom.Element;
048    
049    /**
050     * Creation of an part definition in XML.
051     *
052     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
053     * @version 1.1.0
054     */
055    public class PartTask extends GenericTask
056    {    
057       /**
058        * Constant artifact type for a plugin.
059        */
060        public static final String TYPE = "part";
061    
062       /**
063        * Property key used to identify the plugin title.
064        */
065        public static final String PLUGIN_TITLE_KEY = "project.title";
066        
067       /**
068        * Property key used to identify the plugin description.
069        */
070        public static final String PLUGIN_DESCRIPTION_KEY = "project.description";
071        
072       /**
073        * Property key used to identify a custom plugin handler classname.
074        */
075        public static final String PLUGIN_HANDLER_KEY = "project.plugin.handler";
076        
077        private boolean m_test = false;
078        
079        private File m_output;
080        
081       /**
082        * Set the test build policy.  The default is to include 
083        * the project artifact in the classpath of a created part, however - in a 
084        * test scenario we don't want to do this.  Setting test to true will result
085        * in the association of a local file uri to the project resource.
086        *
087        * @param test true if this is a local test part
088        */
089        public void setTest( boolean test )
090        {
091            m_test = test;
092        }
093    
094       /**
095        * Override the default output destination.
096        *
097        * @param file the overriding destination
098        */
099        public void setDest( File file )
100        {
101            m_output = file;
102        }
103    
104       /**
105        * Task execution.
106        */
107        public void execute()
108        {
109            Resource resource = getResource();
110            Part part = build( resource );
111            writePart( part );
112        }
113        
114       /**
115        * Externalize the part as a deliverable.
116        * @param part the part to be externalized as XML
117        */
118        public void writePart( Part part )
119        {
120            File file = getOutputFile();
121            if( file.exists() )
122            {
123                try
124                {
125                    Part existing = Part.load( file.toURI(), false );
126                    if( part.equals( existing ) )
127                    {
128                        return;
129                    }
130                }
131                catch( Exception e )
132                {
133                    // continue
134                }
135            }
136            
137            log( "Building part: " + file );
138            try
139            {
140                file.createNewFile();
141                final OutputStream output = new FileOutputStream( file );
142                try
143                {
144                    part.encode( output );
145                    checksum( file );
146                    asc( file );
147                }
148                finally
149                {
150                    try
151                    {
152                        output.close();
153                    }
154                    catch( IOException e )
155                    {
156                        e.printStackTrace();
157                    }
158                }
159            }
160            catch( Exception e )
161            {
162                final String error = 
163                  "Part externalization error.";
164                throw new BuildException( error, e );
165            }
166        }
167        
168        private File getOutputFile()
169        {
170            if( null != m_output )
171            {
172                return m_output;
173            }
174            else
175            {
176                Context context = getContext();
177                final String path = context.getLayoutFilename( TYPE );
178                final File deliverables = context.getTargetDeliverablesDirectory();
179                final File parts = new File( deliverables, TYPE + "s" );
180                final File file = new File( parts, path );
181                parts.mkdirs();
182                return file;
183            }
184        }
185        
186       /**
187        * Build the part definition.
188        * @param resource the resource
189        * @return the part
190        */
191        protected Part build( Resource resource )
192        {
193            try
194            {
195                Info info = getInfo( resource );
196                Classpath classpath = getClasspath( resource );
197                Type type = resource.getType( TYPE );
198                Element element = type.getElement();
199                PartDecoder decoder = PartDecoder.getInstance();
200                DefaultLogger logger = new DefaultLogger( getTaskName() );
201                return decoder.build( logger, info, classpath, element, resource );
202            }
203            catch( Throwable e )
204            {
205                final String error = 
206                  "Internal error while attempting to build an external part definition."
207                  + "\nResource: " + resource;
208                throw new BuildException( error, e, getLocation() );
209            }
210        }
211        
212       /**
213        * Construct the info object based on properties declared by the 
214        * supplied resource.
215        * @param resource the resource
216        * @return the info descriptor
217        */
218        protected Info getInfo( Resource resource )
219        {
220            Artifact artifact = resource.getArtifact( TYPE );
221            URI uri = artifact.toURI();
222            String title = resource.getInfo().getTitle();
223            String description = resource.getInfo().getDescription();
224            return new Info( uri, title, description );
225        }
226    
227        private String getTitle( Resource resource )
228        {
229            return resource.getProperty( PLUGIN_TITLE_KEY );
230        }
231    
232        private String getDescription( Resource resource )
233        {
234            return resource.getProperty( PLUGIN_DESCRIPTION_KEY );
235        }
236        
237       /**
238        * Construct the classpath for the supplied resource.
239        * @param resource the resource
240        * @return the classpath 
241        * @exception IOException is an IO error occurs
242        */
243        protected Classpath getClasspath( Resource resource ) throws IOException
244        {
245            URI[] sysUris = getURIs( resource, Category.SYSTEM );
246            URI[] publicUris = getURIs( resource, Category.PUBLIC );
247            URI[] protectedUris = getURIs( resource, Category.PROTECTED );
248            URI[] privateUris = getURIs( resource, Category.PRIVATE, true );
249            return new Classpath( sysUris, publicUris, protectedUris, privateUris );
250        }
251    
252        private URI[] getURIs( Resource resource, Category category ) throws IOException
253        {
254            return getURIs( resource, category, false );
255        }
256        
257        private URI[] getURIs( Resource resource, Category category, boolean self ) throws IOException
258        {
259            Resource[] resources = resource.getClasspathProviders( category );
260            ArrayList list = new ArrayList();
261            for( int i=0; i<resources.length; i++ )
262            {
263                Resource r = resources[i];
264                addURI( list, r );
265            }
266            if( self )
267            {
268                addURI( list, resource );
269            }
270            URI[] uris = (URI[]) list.toArray( new URI[0] );
271            return uris;
272        }
273        
274        private void addURI( List list, Resource resource )  throws IOException
275        {
276            if( resource.isa( "jar" ) )
277            {
278                try
279                {
280                    URI uri = toURI( resource );
281                    list.add( uri );
282                }
283                catch( Exception e )
284                {
285                    final String error = 
286                      "Unexpected error while attempting to resolve resource.";
287                    IOException ioe = new IOException( error );
288                    ioe.initCause( e );
289                    throw ioe;
290                }
291            }
292        }
293        
294        private URI toURI( Resource resource ) throws Exception
295        {
296            if( m_test && resource.equals( getResource() ) )
297            {
298                File local = getContext().getTargetDeliverable( "jar" );
299                return local.toURI();
300            }
301            else
302            {
303                return resource.getArtifact( "jar" ).toURI();
304            }
305        }
306        
307       /**
308        * Get the project definition.
309        * @param project the project
310        * @return the build context
311        */
312        protected Context getContext( Project project )
313        {
314            Context context = (Context) project.getReference( "project.context" );
315            context.init();
316            return context;
317        }
318    }